package sob

import (
	"reflect"
	"strings"
	"sync"
)

var (
	cache sync.Map
)

type rType struct {
	fields []*rField
	fieldM map[string]*rField
}

type rField struct {
	name string
	kind byte
	i    int
}

const (
	kInt byte = iota + 1
	kUint
	kFloat
	kBool
	kString
	kBytes
)

func getReflectType(data any) (*rType, reflect.Value) {
	t := reflect.TypeOf(data)
	v := reflect.ValueOf(data)
	for t.Kind() == reflect.Pointer {
		t = t.Elem()
		v = v.Elem()
	}
	if t.Kind() != reflect.Struct {
		panic("only support simple struct")
	}
	if rt, ok := cache.Load(t); ok {
		return rt.(*rType), v
	}
	rt := &rType{fieldM: map[string]*rField{}}
	n := t.NumField()
	for i := 0; i < n; i++ {
		f := t.Field(i)
		fv := v.Field(i)
		rf := &rField{name: f.Name, i: i}
		switch fv.Interface().(type) {
		case int, int8, int16, int32, int64:
			rf.kind = kInt
		case uint, uint8, uint16, uint32, uint64:
			rf.kind = kUint
		case float64:
			rf.kind = kFloat
		case bool:
			rf.kind = kBool
		case string:
			rf.kind = kString
		case []byte:
			rf.kind = kBytes
		default:
			continue
		}
		if tag := f.Tag.Get(*fTag); tag != "" {
			ss := strings.SplitN(tag, ",", 2)
			if s := ss[0]; s != "" {
				if ss[0] == "-" {
					continue
				}
				rf.name = s
			}
		}
		rt.fields = append(rt.fields, rf)
		rt.fieldM[rf.name] = rf
	}
	cache.Store(t, rt)
	return rt, v
}

/*
type iface struct {
	typ unsafe.Pointer
	ptr unsafe.Pointer
}

func getTypePtr(v any) uintptr {
	i := *(*iface)(unsafe.Pointer(&v))
	return uintptr(i.typ)
}

func test(v any) {
	structPtr := (*iface)(unsafe.Pointer(&v)).ptr
	rt, _ := getReflectType(v)
	rf := rt.fields[1]
	fmt.Println(*(*string)(unsafe.Pointer(uintptr(structPtr) + rf.offset)))
}
*/
